var line = {};

if (typeof(module) != 'undefined') {
    module.exports = line;
}

(function () {
    var toString = Object.prototype.toString;
    
    line.extend = function (target, source) {
        for (var prop in source) {
            target[prop] = source[prop];
        }
        return target;
    };

    line.extend(line, {
        extend:function (target, source) {
            for (var prop in source) {
                target[prop] = source[prop];
            }
            return target;
        },
        each:function (target, callback, context, reverse) {
            if (line.isCollection(target)) {
                var i , len = target.length;
                if (!reverse) {
                    for (i = 0; i < len; i++) {
                        if (callback.call(context || this, target[i], i, target) === false) {
                            return i;
                        }
                    }
                } else {
                    for (i = len - 1; i >= 0; i--) {
                        if (callback.call(context || this, target[i], i, target) === false) {
                            return i;
                        }
                    }
                }
            } else {
                for (var key in target) {
                    if (!line.has(target, key)) {
                        continue;
                    }
                    if (callback.call(context || this, target[key], key, target) === false) {
                        return key;
                    }
                }
            }
        },
        compare:function (target1, target2) {
            var type = line.getType(target1),
                result;
            switch (type) {
                case 'String':
                    result = target1.localeCompare(target2);
                    break;
                case 'Number':
                case 'Boolean':
                case 'Date':
                    result = target1 - target2;
                    break;
            }

            return result && result / Math.abs(result);
        },
        clone:function (target) {
            return line.extend({}, target);
        },
        is:function (target, type) {
            return type === line.getType(target);
        },
        isUndefined:function (target) {
            return line.is(target, 'Undefined');
        },
        isNull:function (target) {
            return line.is(target, 'Null');
        },
        isString:function (target) {
            return line.is(target, 'String');
        },
        isNumber:function (target) {
            return line.is(target, 'Number') && !isNaN(target);
        },
        isBoolean:function (target) {
            return line.is(target, 'Boolean');
        },
        isObject:function (target) {
            return line.is(target, 'Object');
        },
        isArray:Array.isArray || function (target) {
            return line.is(target, 'Array');
        },
        isFunction:function (target) {
            return line.is(target, 'Function');
        },
        isCollection:function (target) {
            return target && target.length >= 0;
        },
        isEmpty:function (target) {
            switch (line.getType(target)) {
                case 'Null':
                    return true;
                case 'Array':
                case 'String':
                    return target.length === 0;
                case 'Object':
                    return line.isEmptyObject(target);
            }
        },
        getType:function (target) {
            if (target === null) {
                return 'Null';
            }
            if (target === void 0) {
                return 'Undefined'
            }

            return toString.call(target).slice(8, -1);
        },
        isEmptyObject:function (target) {
            for (var key in target) {
                return false;
            }
            return true;
        },
        has:function (obj, prop) {
            return obj.hasOwnProperty(prop);
        },
        override:function (target, name, fn) {
            var oldFn = target[name];
            return oldFn && (target[name] = fn(oldFn));
        }
    });
})();
(function (line) {
    var ArrayProto = Array.prototype,
        slice = ArrayProto.slice,
        indexOf=ArrayProto.indexOf;
    line.extend(line, {
        indexOf:(function () {
            if (ArrayProto.indexOf) {
                return function (obj, target) {
                    return ArrayProto.indexOf.call(obj,target);
                };
            } else {
                return function (obj, target) {
                    for (var i = 0, len = obj.length; i < len; i++) {
                        if (target === obj[i]) {
                            return i;
                        }
                    }
                    return -1;
                };
            }
        }()),
        lastIndexOf:(function () {
            if (ArrayProto.lastIndexOf) {
                return function (obj, target) {
                    return ArrayProto.lastIndexOf.call(obj,target);
                };
            } else {
                return function (obj, target) {
                    var i = obj.length;
                    while (i--) {
                        if (target === obj[i]) {
                            return i;
                        }
                    }
                    return -1;
                };
            }
        }()),
        unique:function (array) {
            var table = {},
                result = [],
                tableKey;
            line.each(array, function (val) {
                if (line.isObject(val)) {
                    if (line.indexOf(result, val) === -1) {
                        result.push(val);
                    }
                } else {
                    tableKey = line.getType(val) + val;
                    if (!table[tableKey]) {
                        result.push(val);
                        table[tableKey] = true;
                    }
                }
            });
            return result;
        },
        forEach:(function () {
            if (ArrayProto.forEach) {
                return function (obj, callback, context) {
                    return ArrayProto.forEach.call(obj,callback || null, context);
                };
            } else {
                return function (obj, callback, context) {
                    for (var i = 0, len = obj.length; i < len; i++) {
                        callback.call(context || this, obj[i], i, obj);
                    }
                };
            }
        }()),
        every:(function () {
            if (ArrayProto.every) {
                return function (obj, fn, context) {
                    return ArrayProto.every.call(obj,fn, context);
                };
            } else {
                return function (obj, fn, context) {
                    var result = true;
                    line.each(obj, function (val, key, vObj) {
                        return result = result && fn.call(context, val, key, vObj);
                    });
                    return !!result;
                };
            }
        }()),
        some:(function () {
            if (ArrayProto.some) {
                return function (obj, fn, context) {
                    return ArrayProto.some.call(obj,fn, context);
                };
            } else {
                return function (obj, fn, context) {
                    var result = false;
                    line.each(obj, function (val, key, vObj) {
                        return !(result = result || fn.call(context, val, key, vObj));
                    });
                    return !!result;
                };
            }
        }()),
        map:(function () {
            if (ArrayProto.map) {
                return function (obj, fn, context) {
                    return ArrayProto.map.call(obj,fn, context);
                };
            } else {
                return function (obj, fn, context) {
                    var result = [];
                    line.each(obj, function (val, key, vObj) {
                        result.push(fn.call(context, val, key, vObj));
                    });
                    return result;
                };
            }
        }()),
        filter:(function () {
            if (ArrayProto.filter) {
                return function (obj, fn, context) {
                    return ArrayProto.filter.call(obj,fn, context);
                };
            } else {
                return function (obj, fn, context) {
                    var result = [];
                    line.each(obj, function (val, key, vObj) {
                        if (fn.call(context, val, key, vObj)) {
                            result.push(val);
                        }
                    });
                    return result;
                };
            }
        }())
    });
})(line);
(function (line) {
    /**
     * This is the primary script of webQuery framework layer.
     *
     */

    var global = this,
        identity = 1,
        memberPrefix = '__',
        BaseClass, Property, Event;

    /**
     * (Internal) Base class of all classes defined in webQuery
     * @constructor BaseClass
     */
    BaseClass = function () {
    };
    BaseClass.prototype = {
        inherited:function () {
            var base = this.inherited.caller._base;
            if (base) {
                return base.apply(this, arguments);
            }
            else {
                console.log('Method "' + this.inherited.caller._name + '" is not an inherited method.');
                return null;
            }
        },
        getMember:function (name, type) {
            var member = this[memberPrefix + name];
            switch (type) {
                case 'event':
                    member = member instanceof Event ? member : null;
                    break;
                case 'property':
                    member = member instanceof Property ? member : null;
                    break;
                case 'method':
                    member = this[name];
                    member = line.isFunction(this[name]) ? member : null;
                    break;
            }

            if (!member) {
                console.log('object has no ' + (type || 'member') + ' "' + name + '".');
            }

            return member;
        },
        hasMember:function (name, type) {
            var member = this[memberPrefix + name];
            switch (type) {
                case 'event':
                    return member instanceof Event;
                case 'property':
                    return member instanceof Property;
                case 'method':
                    return line.isFunction(this[name]);
                default:
                    return !line.isUndefined(member);
            }
        },
        hasEvent:function (name) {
            return this.hasMember(name, 'event');
        },
        hasProperty:function (name) {
            return this.hasMember(name, 'property');
        },
        hasMethod:function (name) {
            return this.hasMember(name, 'method');
        },
        getProperty:function (name) {
            var prop = this.getMember(name, 'property');
            if (prop) {
                return prop.getter(this);
            }
        },
        setProperty:function (name, value) {
            var prop = this.getMember(name, 'property');
            if (prop) {
                return prop.setter(this, value);
            }
        },
        prop:function (name, value) {
            if (line.isUndefined(value)) {
                return this.getProperty(name);
            }
            else {
                this.setProperty(name, value);
            }
        },
        on:function (name, handler, context) {
            var event = this.getMember(name, 'event');
            if (event) {
                event.addHandler(this, handler, context, this);
            }
        },
        off:function (name, handler, context) {
            var event = this.getMember(name, 'event');
            if (event) {
                if (line.isUndefined(handler)) {
                    event.clearHandlers(this);
                }
                else {
                    event.removeHandler(this, handler, context);
                }
            }
        },
        fire:function (name, data) {
            var event = this.getMember(name, 'event');
            if (event) {
                event.fireEvent(this, data);
            }
        },
        invoke:function (name) {
            var method = this.getMember(name, 'method');
            if (method) {
                method.apply(this, Array.prototype.slice.call(arguments, 1));
            }
        }
    };

    /**
     * (Internal) Implements the Property mechanism
     * @param name
     * @param config
     * @constructor Property
     */
    Property = function (name, config) {
        line.extend(this, config);
        this.name = name;
        this.map = {};
        if (this.readonly) {
            var setter = this.setter, self = this;
            this.setter = function (owner, value) {
                if (owner.initializing) {
                    setter.call(self, owner, value);
                }
                else {
                    console.log('Cannot set readonly property "' + name + '".');
                }
            };
        }
    };
    Property.prototype = {
        constructor:Property,
        getter:function (owner) {
            return this.map[owner.$id] || this.defaultValue;
        },
        setter:function (owner, value) {
            this.map[owner.$id] = value;
        }
    };

    /**
     * (Internal) Implement the Event mechanism
     * @param name
     * @constructor Event
     */
    Event = function (name) {
        this.name = name;
        this.map = {};
    };
    Event.prototype = {
        constructor:Event,
        addHandler:function (owner, handler, context) {
            var subscribers = this.map[owner.$id] = this.map[owner.$id] || [];
            subscribers.push({
                owner:owner,
                handler:handler,
                context:context
            });
        },
        removeHandler:function (owner, handler, context) {
            var subscribers = this.map[owner.$id];
            var n = -1;
            if (subscribers) {
                line.each(subscribers, function (item, index) {
                    if (item.handler === handler && item.context === context) {
                        n = index;
                    }
                });

                if (n >= 0) {
                    subscribers.splice(n, 1);
                }
            }
        },
        clearHandlers:function (owner) {
            this.map[owner.$id] = [];
        },
        fireEvent:function (owner, data) {
            var subscribers = this.map[owner.$id];
            if (subscribers) {
                line.each(subscribers, function (item) {
                    item.handler.call(item.context, item.owner, data);
                });
            }
        }
    };

    line.extend(line, {
        namespace:function (path) {
            var tokens = path.split('.'), token;
            var target = global;

            while (token = tokens.shift()) {
                target = target[token] = target[token] || {};
            }

            return target;
        },
        getClass:function (type) {
            var tokens = type.split('.'), token;
            var target = global;

            while ((token = tokens.shift()) && target) {
                target = target[token];
            }

            if (target && target.$type) {
                return target;
            }
            else {
                console.log('Cannot find class "' + type + '".');
                return null;
            }
        },
        define:function (type, base, members) {
            members = members || {};
            var dotIndex = type.lastIndexOf('.');
            var ns, clsName, prototype;
            var modifier = members.modifier;
            var events = members.events || [];
            var props = members.properties || {};
            var methods = members.methods || {};
            var result;

            if (dotIndex > 0) {
                ns = line.namespace(type.slice(0, dotIndex));
                clsName = type.slice(dotIndex + 1);
            }
            else {
                ns = global;
                clsName = type;
            }

            if (base && base.$sealed) {
                throw new Error('Cannot inherit from sealed class "' + base.$type + '".');
            }

            function Class() {
                if (!Class.inheriting) {
                    if (Class.$abstract) {
                        throw new Error('Cannot initialize abstract class "' + type + '".');
                    }
                    else {
                        this.$id = identity++;
                    }
                }

                if (this.init) {
                    this.initializing = true;
                    this.init.apply(this, arguments);
                    delete this.initializing;
                }
            }

            if (line.isFunction(base)) {
                base.inheriting = true;
                prototype = new base;
                delete base.inheriting;
            }
            else {
                prototype = new BaseClass;
            }

            prototype.constructor = Class;
            prototype.$type = type;
            prototype.$base = base || null;
            Class.prototype = prototype;
            Class.$type = type;

            if (modifier) {
                Class['$' + modifier] = true;
            }

            line.forEach(events, function (value) {
                prototype[memberPrefix + value] = new Event(value);
            });

            line.each(props, function (value, key) {
                prototype[memberPrefix + key] = new Property(key, value);
            });

            line.each(methods, function (value, key) {
                value._name = key;
                if (prototype[key]) {
                    value._base = prototype[key];
                }
                prototype[key] = value;
            });

            if (Class.$static) {
                result = ns[clsName] = new Class;
            }
            else {
                result = ns[clsName] = Class;
            }

            return result;
        },
        extendEvent:function (cls, name) {
            cls.prototype[memberPrefix + name] = new Event();
        },
        extendProperty:function (cls, name, config) {
            cls.prototype[memberPrefix + name] = new Property(config);
        },
        extendMethod:function (cls, name, method) {
            if (line.isFunction(method)) {
                cls.prototype[memberPrefix + name] = method;
            }
        }
    });

})(line);
(function (line) {
    line.extend(line, {
        formatDate:function (date, format) {
            var week = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'];
            var selector = {
                'yyyy':date.getFullYear(),
                'yy':date.getFullYear().toString().slice(2),
                'mm':date.getMonth(),
                'dd':date.getDate(),
                'hh':date.getHours(),
                'min':line.padLeft(date.getMinutes().toString(), 2, '0'),
                'ss':line.padLeft(date.getSeconds().toString(), 2, '0'),
                'week':week[date.getDay()]
            };
            return format.replace(/yyyy|yy|mm|dd|hh|min|ss|week/g, function (key) {
                return selector[key];
            });
        },
        now:Date.now || function(){
            return +new Date;
        }
    });
})(line);
(function (line) {
    var slice = Array.prototype.slice;
    line.extend(line, {
        bind:(function () {
            var nativeBind = Function.prototype.bind;
            return nativeBind ? function (fn, context) {
                return nativeBind.apply(fn, slice.call(arguments, 1));
            } : function (fn, context) {
                if (arguments.length < 2 && line.isUndefined(context)) {
                    return fn;
                }
                var args = slice.call(arguments, 2);
                return function () {
                    var inArgs = slice.call(arguments, 0);
                    return fn.apply(context, args.concat(inArgs));
                }
            }
        }()),
        defer:function (fn) {
            var args = slice.call(arguments, 1);
            return line.delay(fn, 0, args);
        },
        delay:function (fn, wait) {
            var args = slice.call(arguments, 2);
            return setTimeout(function () {
                fn.apply(null, args);
            }, wait);
        }
    });
})(line);
(function (line) {
    line.extend(line, {
        parseJSON:function(target,callback){
            return JSON.parse(target,callback);
        },
        stringify:function(object, replacer, spacer){
            return JSON.stringify(object, replacer, spacer);
        }
    });
})(line);
(function (line) {
    line.extend(line, {
        keys:function (obj) {
            var result = [];
            line.each(obj, function (val, key) {
                result.push(key);
            });
            return result;
        },
        values:function (obj) {
            var result = [];
            line.each(obj, function (val) {
                result.push(val);
            });
            return result;
        }
    });
})(line);
(function (line) {
    var StringProto = String.prototype;
    line.extend(line, {
        contains:function (string1, string2) {
            return string1.indexOf(string2) > -1;
        },
        startsWith:function (string1, string2) {
            return string1.indexOf(string2) === 0;
        },
        endsWith:function (string1, string2) {
            var index = string1.lastIndexOf(string2);
            return index > 0 && index == (string1.length - string2.length);
        },
        camelize:function (string) {
            return string.replace(/-(\w)/ig, function (str, $1) {
                return $1.toUpperCase();
            });
        },
        decamelize:function (string) {
            return string.replace(/([A-Z])/g, function ($1) {
                return '-' + $1.toLowerCase();
            });
        },
        capitalize:function (string) {
            return string.charAt(0).toUpperCase() + string.slice(1);
        },
        format:function () {
            var format = arguments[0],
                args = Array.prototype.slice.call(arguments, 1);
            return format.replace(/\{(\d+)\}/g, function (m, i) {
                return args[i];
            });
        },
        padLeft:function (string, length, padChar) {
            return new Array(length - string.length + 1).join(padChar || ' ') + string;
        },
        padRight:function (string, length, padChar) {
            return string + new Array(length - string.length + 1).join(padChar || ' ');
        },
        //jquery:rtrim = /^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,
        trimLeft:(function () {
            return StringProto.trimLeft ? function (string) {
                return string.trimLeft();
            } : function (string) {
                return string.replace(/^\s+/, '');
            }
        }()),
        trimRight:(function () {
            return StringProto.trimRight ? function (string) {
                return string.trimRight();
            } : function (string) {
                return string.replace(/\s+$/, '');
            }
        }()),
        trim:(function () {
            return StringProto.trim ? function (string) {
                return string.trim();
            } : function (string) {
                return line.trimRight(line.trimLeft(string));
            }
        }())
    });
})(line);